package com.companyname.demo.cod.scheduled;

import com.companyname.demo.cod.util.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 测试场景：
 * 一组任务中，每个任务的执行耗时差异很大。要求同一个类型的任务不能并行执行（涉及数据库的一致性），且耗时长的任务不能影响其他任务的正常执行
 * 分析：
 * 如果将它们放在一个线程池中，那么耗时长的任务最终会霸占池中所有的可用线程，导致其他任务都堵塞在线程池队列中，或者队列满后被线程池拒绝执行。
 * 测试：
 * 使用固定线程池FixedThreadPool，设置为异步对以上情形进行测试：
 * 如果corePoolSize较小，那么线程很快被耗尽，抛出拒绝异常；
 * 如果corePoolSize较大，那么同一个类型的任务就会被多个线程同时执行。
 */
@Service
@Slf4j
public class FixedThreadPoolScheduledTask {
    @Autowired
    private AsyncExecutorService asyncExecutorService;

    /**
     * 10s定期从数据库获取任务集合，使用Fixed线程池异步处理任务
     */
    @Scheduled(fixedRate = 10000)
    public void test() {
        log.info("--------------定时任务到期开始--------------");
        //组装5个任务
        List<Map<String, Object>> taskList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            String taskName = "task" + (i + 1);
            Map<String, Object> task = new HashMap<>();
            task.put("taskName", taskName);
            taskList.add(task);
        }

        //从容器中获取配置类ThreadPoolConfig向容器中注册的一个name为fixedThreadPool的固定线程池TaskExecutor
        ThreadPoolTaskExecutor taskExecutor = (ThreadPoolTaskExecutor) SpringUtil.getBean("fixedThreadPool");
        ThreadPoolExecutor executor = taskExecutor.getThreadPoolExecutor();

        //遍历任务集合，使用异步服务处理
        for (Map<String, Object> eachTask : taskList) {
            try {
                asyncExecutorService.fixedThreadPoolAsyncExecute(eachTask);
            } catch (Exception e) {
                log.error("*********{}任务使用线程池异步处理时发生异常:{}", eachTask.get("taskName"), e.getMessage());
                log.info("发生异常时的线程池执行器Executor信息：[{}]", executor.toString());
            }
        }

        /*
        测试结果分析：
        1.某同一个任务，会被两个线程同时执行，这两个线程的时间线有交集，不满足串行要求
        2.耗时高于定时任务周期的task，比如task4，耗时15s，执行完后应该紧接着执行，执行周期应该为15s。但实际情况不符，有一次24s后才处理
        3.耗时低于定时任务周期的task，比如task3，耗时6s，执行周期应该为定时任务的10s，但实际情况不符，有一次15s后才处理
         */
    }


}
